// Copyright 2008 Radu Stefan
//
// This design is licensed under the GPL
//

module vga(
	input clk,
	input nreset,
	input btnrt,
	input btnle,
	input btncr,
	input btnup,
	output led0,
	output led1,
	output led2,
	output led3,
	output VGA_CLK,
	output [7:0] VGA_R,
	output [7:0] VGA_G,
	output [7:0] VGA_B,
	output VGA_HSYN,
	output VGA_VSYN,
	output VGA_COMP,
	output VGA_BLNK
	);

parameter HACTIVE  = 1280;
parameter HRGHTM   =   52;
parameter HRETRACE =  120;
parameter HLEFTM   =  256;

parameter VACTIVE  = 1024;
parameter VBTMM    =    3;
parameter VRETRACE =    5;
parameter VTOPM    =   42;
	
parameter COLUMNS    = 160;
parameter [3:0] CHARHEIGHTMO =  11; // minus one

parameter CSYNCPOLARITY = 0;
parameter HSYNCPOLARITY = 1;
parameter VSYNCPOLARITY = 1;
	
reg [5:0] hsyn;
reg [5:0] vsyn;
reg [5:0] blnk;
reg rl,gl,bl,rh,gh,bh;
wire comp, reset;

wire vgaclk, nclk, locked;

reg [1:0] hstate; // 0 = actual disp, 1 right margin, 2 retrace, 3 left margin
reg [1:0] vstate;
reg [10:0] hcount;
reg [10:0] hlimit;
reg [10:0] vcount;
reg [10:0] vlimit;

reg [7:0] cch;
reg [7:0] ccl;
wire [7:0] cclr; // character line from ram
wire [15:0] vcc;
reg [6:0] crow;
reg [3:0] clin;
reg [7:0] cdat;
reg [3:0] ccsel;
// synthesis attribute IOB of VGA_R is TRUE
// synthesis attribute IOB of VGA_G is TRUE
// synthesis attribute IOB of VGA_B is TRUE
// synthesis attribute IOB of VGA_COMP is TRUE
// synthesis attribute IOB of VGA_VSYN is TRUE
// synthesis attribute IOB of VGA_HSYN is TRUE
// synthesis attribute IOB of VGA_BLNK is TRUE

// synthesis attribute KEEP of VGA_R is "TRUE"
// synthesis attribute KEEP of VGA_G is TRUE
// synthesis attribute KEEP of VGA_B is TRUE
// synthesis attribute KEEP of VGA_COMP is TRUE
// synthesis attribute KEEP of VGA_VSYN is TRUE
// synthesis attribute KEEP of VGA_HSYN is TRUE
// synthesis attribute KEEP of VGA_BLNK is TRUE

	assign vgaclk=clk;
	assign nclk=!vgaclk;
	
	assign led0=1'b0;
	assign led1=1'b1;
	assign led2=reset;
	assign led3=locked;
	assign comp=blnk[0] ^ CSYNCPOLARITY;
	assign reset=!nreset;
	
	initial begin
		// we want a nice gray as default
		rl=1'b1;
		gl=1'b1;
		bl=1'b1;
		rh=1'b0;
		gh=1'b0;
		bh=1'b0;		
	end
	
	always @(posedge vgaclk) begin
		if (reset) begin
			hstate <= 2'd3;
			vstate <= 2'd3;
			hcount <= 11'b0;
			vcount <= 11'b0;
			hlimit <= 11'b1;
			vlimit <= 11'b1;
		end else begin
			blnk[5] <= ((hstate==2'b0) && (vstate==2'b0));
			hsyn[5] <= (hstate==2'b10) ^ HSYNCPOLARITY;
			vsyn[5] <= (vstate==2'b10) ^ VSYNCPOLARITY;
			blnk[4:0] <= blnk[5:1];
			hsyn[4:0] <= hsyn[5:1];
			vsyn[4:0] <= vsyn[5:1];
			hcount <= hcount+1;
			if (hcount == hlimit) begin
				hcount <= 0;
				hstate <= hstate+1;
				case (hstate)
				2'b11: hlimit <= HACTIVE-1;
				2'b00: hlimit <= HRGHTM-1;
				2'b01: hlimit <= HRETRACE-1;
				2'b10: hlimit <= HLEFTM-1;
				endcase				
			end
			if (hcount == hlimit && hstate==2'b01) begin
				vcount <= vcount+1;
				clin   <= clin+1;
				if (clin == CHARHEIGHTMO) begin
					clin <= 0;
					crow <= crow+1;
				end
				if (vcount == vlimit) begin
					crow   <= 0;
					clin   <= 0;
					vstate <= vstate+1;
					vcount <= 0;
					case (vstate)
					2'b11: vlimit <= VACTIVE-1;					       
					2'b00: vlimit <= VBTMM-1;
					2'b01: vlimit <= VRETRACE-1;
					2'b10: vlimit <= VTOPM-1;
					endcase
				end
			end
		end
	end

	charram cim (
		.addra({cch,clin}), // Bus [11 : 0] 
		.addrb(10'b0), // Bus [9 : 0] 
		.clka(vgaclk),
		.clkb(vgaclk),
		.dinb(31'b0), // Bus [31 : 0] 
		.douta(cclr), // Bus [7 : 0] 
		//.doutb(doutb), // Bus [31 : 0] 
		.web(1'b0));

	videoram vim (
		.clka(vgaclk),
		.clkb(vgaclk),
		.addra(crow*160+hcount[10:3]),
		.addrb(14'b0),
		.dinb(16'b0),
		.douta(vcc),
		.web(1'b0)
		);
		
	always @(posedge vgaclk) begin		
		if (hcount[2:0]==3'b010) begin
			cch <= vcc[7:0];
		end
		
		cdat <= { cdat[6:0], 1'b0 };
		
		if (hcount[2:0]==3'b100) begin
			cdat <= cclr;
			ccl <= vcc[15:8];
		end
		
		ccsel = cdat[7]?ccl[3:0]:ccl[7:4];
		
		if (!btnle) ccsel=ccl[3:0];
		if (!btnrt) ccsel=ccl[7:4];
		if (!btncr) ccsel=4'b0;
		
		rh <= ccsel[3] & ccsel[2];
		rl <= ccsel[2];
		gh <= ccsel[3] & ccsel[1];
		gl <= ccsel[1];
		bh <= ccsel[3] & ccsel[0];
		bl <= ccsel[0];
				
		//;
	end
/*
	DCM #(
		.CLKDV_DIVIDE(10.0),
		.CLKFX_DIVIDE(10),
		.CLKFX_MULTIPLY(11),
		.CLKIN_DIVIDE_BY_2("FALSE"),
		.CLKIN_PERIOD(10.0),
		.CLKOUT_PHASE_SHIFT("NONE"),
		.CLK_FEEDBACK("NONE"),
		.DFS_FREQUENCY_MODE("LOW"),  // LOW frequency mode for frequency synthesis
		.DLL_FREQUENCY_MODE("LOW"),
		.DUTY_CYCLE_CORRECTION("TRUE"),
		.PHASE_SHIFT(0),     // Amount of fixed phase shift from -255 to 255
		.STARTUP_WAIT("FALSE")   // Delay configuration DONE until DCM LOCK FALSE
	)
	DCM_pixclk(
		.CLKFX(vgaclk),   // DCM CLK synthesis out (M/D)
		.CLKFX180(nclk), // 180 degree CLK synthesis out
		.LOCKED(locked), // DCM LOCK status output
		.CLKIN(clk)   // Clock input (from IBUFG, BUFG or DCM)
		//.RST(!nreset)        // DCM asynchronous reset input
	);
	
	*/
	
	OFDDRRSE OCKB  (.Q(VGA_CLK), .C0(clk), .C1(nclk), .CE(1'b1), .R(1'b0), .D0(1'b0), .D1(1'b1), .S(1'b0));
	//assign VGA_CLK=nclk;
	

	FDRE FDRE_HSYN (.Q(VGA_HSYN), .C(vgaclk), .CE(1'b1), .R(rst), .D(hsyn))     /* synthesis syn_useioff = 1 */;
	FDRE FDRE_VSYN (.Q(VGA_VSYN), .C(vgaclk), .CE(1'b1), .R(rst), .D(vsyn))     /* synthesis syn_useioff = 1 */;
	FDRE FDRE_COMP (.Q(VGA_COMP), .C(vgaclk), .CE(1'b1), .R(rst), .D(comp))     /* synthesis syn_useioff = 1 */;
	FDRE FDRE_BLNK (.Q(VGA_BLNK), .C(vgaclk), .CE(1'b1), .R(rst), .D(blnk))     /* synthesis syn_useioff = 1 */;
		
	FD FD_R0        (.Q(VGA_R[0]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R1        (.Q(VGA_R[1]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R2        (.Q(VGA_R[2]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R3        (.Q(VGA_R[3]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R4        (.Q(VGA_R[4]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R5        (.Q(VGA_R[5]), .C(vgaclk), .D(rh))       /* synthesis syn_useioff = 1 */;
	FD FD_R6        (.Q(VGA_R[6]), .C(vgaclk), .D(rl))       /* synthesis syn_useioff = 1 */;
	FD FD_R7        (.Q(VGA_R[7]), .C(vgaclk), .D(rl))       /* synthesis syn_useioff = 1 */;
	
	FD FD_G0        (.Q(VGA_G[0]), .C(vgaclk), .D(btncr?gh:hcount[0]))       /* synthesis syn_useioff = 1 */;
	FD FD_G1        (.Q(VGA_G[1]), .C(vgaclk), .D(btncr?gh:hcount[1]))       /* synthesis syn_useioff = 1 */;
	FD FD_G2        (.Q(VGA_G[2]), .C(vgaclk), .D(btncr?gh:hcount[2]))       /* synthesis syn_useioff = 1 */;
	FD FD_G3        (.Q(VGA_G[3]), .C(vgaclk), .D(btncr?gh:hcount[3]))       /* synthesis syn_useioff = 1 */;
	FD FD_G4        (.Q(VGA_G[4]), .C(vgaclk), .D(btncr?gh:hcount[4]))       /* synthesis syn_useioff = 1 */;
	FD FD_G5        (.Q(VGA_G[5]), .C(vgaclk), .D(btncr?gh:hcount[5]))       /* synthesis syn_useioff = 1 */;
	FD FD_G6        (.Q(VGA_G[6]), .C(vgaclk), .D(btncr?gl:hcount[6]))       /* synthesis syn_useioff = 1 */;
	FD FD_G7        (.Q(VGA_G[7]), .C(vgaclk), .D(btncr?gl:hcount[7]))       /* synthesis syn_useioff = 1 */;
	
	FD FD_B0        (.Q(VGA_B[0]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B1        (.Q(VGA_B[1]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B2        (.Q(VGA_B[2]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B3        (.Q(VGA_B[3]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B4        (.Q(VGA_B[4]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B5        (.Q(VGA_B[5]), .C(vgaclk), .D(bh))       /* synthesis syn_useioff = 1 */;
	FD FD_B6        (.Q(VGA_B[6]), .C(vgaclk), .D(bl))       /* synthesis syn_useioff = 1 */;
	FD FD_B7        (.Q(VGA_B[7]), .C(vgaclk), .D(bl))       /* synthesis syn_useioff = 1 */;
	
endmodule
